/* Copyright(C) 2006-2008 Dave Sexton  *
 * http://www.codeplex.com/aip         *
 * http://www.codeplex.com/aip/license *
 ***************************************/
using System;
using System.Collections.Generic;
using System.Text;
using DaveSexton.Web.Controls.Resources;

namespace DaveSexton.Web.Controls
{
	/// <summary>
	/// Generates collections of random <see cref="Interval">Intervals</see>.
	/// </summary>
	/// <seealso cref="Interval"/>
	/// <seealso cref="TranslucentBarsOverlayAutoInputProtectionFilterProvider"/>
	/// <seealso cref="RandomString"/>
	/// <threadsafety static="true"/>
	public static class RandomIntervals
	{
		/// <summary>
		/// Returns an <see cref="IEnumerable{T}"/> of <see cref="Interval"/> structures that are randomly generated.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxCount"/> is less than <paramref name="minCount"/>, 
		/// <paramref name="minCount"/> is less than zero, <paramref name="minIndividualWidth"/> is less than one or 
		/// <paramref name="maxBoundingWidth"/> is less than one.</exception>
		/// <param name="minCount">The minimum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="maxCount">The maximum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="minIndividualWidth">The minimum value of the randomly generated width for each <see cref="Interval" />.</param>
		/// <param name="maxBoundingWidth">The maximum value of the combined widths of each randomly generated <see cref="Interval" />.</param>
		/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Interval">Interval</see> structures that are randomly generated.</returns>
		public static IEnumerable<Interval> Create(int minCount, int maxCount, int minIndividualWidth, int maxBoundingWidth)
		{
			return CreateInternals(0, minCount, maxCount, minIndividualWidth, maxBoundingWidth, true);
		}

		/// <summary>
		/// Returns an <see cref="IEnumerable{T}"/> of <see cref="Interval"/> structures that are randomly generated.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxCount"/> is less than <paramref name="minCount"/>, 
		/// <paramref name="minCount"/> is less than zero, <paramref name="minIndividualWidth"/> is less than one or
		/// <paramref name="maxBoundingWidth"/> is less than one.</exception>
		/// <param name="offset">A value added to the <see cref="Interval.Position">Position</see> of each <see cref="Interval"/> that 
		/// is generated.  Any number that doesn't cause overflow is acceptable, including negative values and zero.</param>
		/// <param name="minCount">The minimum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="maxCount">The maximum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="minIndividualWidth">The minimum value of the randomly generated width for each <see cref="Interval" />.</param>
		/// <param name="maxBoundingWidth">The maximum value of the combined widths of each randomly generated <see cref="Interval" />.</param>
		/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Interval">Interval</see> structures that are randomly generated.</returns>
		public static IEnumerable<Interval> Create(int offset, int minCount, int maxCount, int minIndividualWidth, int maxBoundingWidth)
		{
			return CreateInternals(offset, minCount, maxCount, minIndividualWidth, maxBoundingWidth, true);
		}

		/// <summary>
		/// Returns an <see cref="IEnumerable{T}"/> of <see cref="Interval"/> structures that are randomly generated, ensuring that 
		/// no <see cref="Interval.Width">Widths</see> overlap.
		/// </summary>
		/// <remarks>
		/// <see cref="ArgumentOutOfRangeException"/> is thrown when 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width; however, since the maximum width is a randomized 
		/// calculation, it's possible that one invocation may succeed while another will fail, with the same arguments.
		/// </remarks>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxCount"/> is less than <paramref name="minCount"/>, 
		/// <paramref name="minCount"/> is less than zero, <paramref name="minIndividualWidth"/> is less than one, 
		/// <paramref name="maxBoundingWidth"/> is less than one, or 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width.</exception>
		/// <param name="minCount">The minimum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="maxCount">The maximum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="minIndividualWidth">The minimum value of the randomly generated width for each <see cref="Interval" />.</param>
		/// <param name="maxBoundingWidth">The maximum value of the combined widths of each randomly generated <see cref="Interval" />.</param>
		/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Interval">Interval</see> structures that are randomly generated.</returns>
		public static IEnumerable<Interval> CreateIsolated(int minCount, int maxCount, int minIndividualWidth, int maxBoundingWidth)
		{
			return CreateInternals(0, minCount, maxCount, minIndividualWidth, maxBoundingWidth, false);
		}

		/// <summary>
		/// Returns an <see cref="IEnumerable{T}"/> of <see cref="Interval"/> structures that are randomly generated, ensuring that 
		/// no <see cref="Interval.Width">Widths</see> overlap.
		/// </summary>
		/// <remarks>
		/// <see cref="ArgumentOutOfRangeException"/> is thrown when 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width; however, since the maximum width is a randomized 
		/// calculation, it's possible that one invocation may succeed while another will fail, with the same arguments.
		/// </remarks>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxCount"/> is less than <paramref name="minCount"/>, 
		/// <paramref name="minCount"/> is less than zero, <paramref name="minIndividualWidth"/> is less than one, 
		/// <paramref name="maxBoundingWidth"/> is less than one, or 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width.</exception>
		/// <param name="offset">A value added to the <see cref="Interval.Position">Position</see> of each <see cref="Interval"/> that 
		/// is generated.  Any number that doesn't cause overflow is acceptable, including negative values and zero.</param>
		/// <param name="minCount">The minimum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="maxCount">The maximum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="minIndividualWidth">The minimum value of the randomly generated width for each <see cref="Interval" />.</param>
		/// <param name="maxBoundingWidth">The maximum value of the combined widths of each randomly generated <see cref="Interval" />.</param>
		/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Interval">Interval</see> structures that are randomly generated.</returns>
		public static IEnumerable<Interval> CreateIsolated(int offset, int minCount, int maxCount, int minIndividualWidth, int maxBoundingWidth)
		{
			return CreateInternals(offset, minCount, maxCount, minIndividualWidth, maxBoundingWidth, false);
		}

		/// <summary>
		/// Returns an <see cref="IEnumerable{T}"/> of <see cref="Interval"/> structures that are randomly generated, optionally ensuring that 
		/// no <see cref="Interval.Width">Widths</see> overlap.
		/// </summary>
		/// <remarks>
		/// <see cref="ArgumentOutOfRangeException"/> is thrown when <paramref name="allowOverlap"/> is <see langword="false"/> and 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width; however, since the maximum width is a randomized 
		/// calculation, it's possible that one invocation may succeed while another will fail, with the same arguments.
		/// </remarks>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxCount"/> is less than <paramref name="minCount"/>, 
		/// <paramref name="minCount"/> is less than zero, <paramref name="minIndividualWidth"/> is less than one, 
		/// <paramref name="maxBoundingWidth"/> is less than one, or <paramref name="allowOverlap"/> is <see langword="false"/> and 
		/// <paramref name="minIndividualWidth"/> is greater than the calculated maximum width.</exception>
		/// <param name="offset">A value added to the <see cref="Interval.Position">Position</see> of each <see cref="Interval"/> that 
		/// is generated.  Any number that doesn't cause overflow is acceptable, including negative values and zero.</param>
		/// <param name="minCount">The minimum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="maxCount">The maximum number of <see cref="Interval">Intervals</see> to be generated.</param>
		/// <param name="minIndividualWidth">The minimum value of the randomly generated width for each <see cref="Interval" />.</param>
		/// <param name="maxBoundingWidth">The maximum value of the combined widths of each randomly generated <see cref="Interval" />.</param>
		/// <param name="allowOverlap">Specifies whether the <see cref="Interval.Width">Widths</see> of <see cref="Interval">Intervals</see> may 
		/// overlap.</param>
		/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Interval">Interval</see> structures that are randomly generated.</returns>
		private static IEnumerable<Interval> CreateInternals(int offset, int minCount, int maxCount, int minIndividualWidth, int maxBoundingWidth, 
			bool allowOverlap)
		{
			if (minCount < 0)
				throw new ArgumentOutOfRangeException("minCount", minCount, Errors.PositiveIntOrZeroRequired);

			if (maxCount < minCount)
				throw new ArgumentOutOfRangeException("maxCount", maxCount, Errors.RandomInterval_MaxLessThanMin);

			if (minIndividualWidth < 1)
				throw new ArgumentOutOfRangeException("minIndividualWidth", minIndividualWidth, Errors.PositiveIntRequired);

			if (maxBoundingWidth < 1)
				throw new ArgumentOutOfRangeException("maxBoundingWidth", maxBoundingWidth, Errors.PositiveIntRequired);

			Random rnd = new Random();

			int count = rnd.Next(minCount, maxCount + 1);
			int maxStep = maxBoundingWidth / count;

			// Check for the possibility of overflow if allowOverlap is false.
			// Example of overflow in subsequent interval widths: {0-5} {4-9} {7-10}
			if (!allowOverlap && minIndividualWidth >= maxStep)
				throw new ArgumentOutOfRangeException("minIndividualWidth", minIndividualWidth, Errors.RandomInterval_Overlap);

			//System.Diagnostics.Debug.WriteLine(count, "Random interval count" + ((allowOverlap) ? string.Empty : " (isolated)"));
			//System.Diagnostics.Debug.Indent();

			for (int i = 0; i < count; i++)
			{
				// randomize position and width of the Interval at the current index
				int start = i * maxStep;
				int position = rnd.Next(0, maxStep);
				int width = rnd.Next(minIndividualWidth, Math.Max(minIndividualWidth, (allowOverlap)
					? maxBoundingWidth - start
					: maxStep - position));

				Interval interval = new Interval(offset + start + position, width);

				//System.Diagnostics.Debug.WriteLine(interval);

				yield return interval;
			}

			//System.Diagnostics.Debug.Unindent();
		}
	}
}
